#include <string.h> 
#include "TextDoc.h"

/* 
 * On return 0, no move is needed.
 * On return 1, the insert postion is returned in *line and *col
 */
int GetBlockMoveInsertPos(unsigned int* line, unsigned int* col, CTextSelection* sel)
{
    assert (line);
    assert (col);
    assert (sel);
    assert (sel->Selected());

    int ret = sel->ComparePosWithMe(*line, *col);
    if (ret == 0) {
        return 0;
    }
     
    if (ret < 0) {
        return 1;
    }
    unsigned sl, sc, bl, bc;
    bool selected = sel->GetAscendantSel(&sl, &sc, &bl, &bc);
    assert (selected);

    if (*line == bl && *col == bc) {
        /* No need to move, if user want to move immediately after the selection*/
        return 0;
    }

    if (*line == bl) {
        *col = *col - bc + sc;
    }
    *line = *line - (bl - sl);
    return 1;
}



//-------------------
CTextDoc::CTextDoc() : CVarLines(DOC_INIT_LINES)
{
    int i;
    for (i = 1; i < DOC_INIT_LINES; i++) {
        m_pArray[i] = NULL;
    }
    // The caret is at the first line. So doc initialized with 1 line.
    m_nValidLines = 1; 
    m_pArray[0] = new CTextLine;  
    m_crlf[0] = '\0';
    m_dirty = false;
    m_stack = NULL;
    m_pBkPtMgr = NULL;
}

CTextDoc::~CTextDoc()
{
    unsigned int i;
    for (i = 0; i < m_nCount; i++) {
        if (m_pArray[i] == NULL) {
            break;
        }
        delete m_pArray[i];
    }
}

int CTextDoc::InsertTextAt (unsigned int row, unsigned int col, char* text,
		                   unsigned int* newrow/*=NULL*/, 
						   unsigned int* newcol/*=NULL*/)
{
    unsigned int endrow, endcol;
    CEditOperation* op = new CEditOperation;
    op->CopyString(text);

    int ret = InsertTextHelper (row, col, text, &endrow, &endcol);
    if (newrow) {
	    *newrow = endrow;
    }
    if (newcol) {
	    *newcol = endcol;
    }
    if (ret != 0) {
        delete (op);
        return ret;
    }

    if (!m_stack) {
        delete (op);
        return 0;
    }

    op->m_startline = row;
    op->m_startcol = col;
    op->m_endline = endrow;
    op->m_endcol = (endcol == 0 ? 0 : endcol-1);
    op->m_operation = Add;

    m_stack->AddOperation(op);
    return 0;
}

int CTextDoc::InsertTextAtByStack (unsigned int row, unsigned int col, char* text)
{
    return InsertTextHelper (row, col, text);
}

int CTextDoc::InsertTextHelper (unsigned int row, unsigned int col, char* text,
		                   unsigned int* newrow/*=NULL*/, unsigned int* newcol/*=NULL*/)
{
    assert (m_nValidLines >= 1);

    row = (row == LAST_LINE ? m_nValidLines -1 : row);
    col = (col == EOL ? strlen(m_pArray[row]->GetArrayPtr()) : col);

    assert (row < m_nValidLines);
    assert (col <= strlen(GetLineText(row)));

    char* curline = text;
    char* nextline = NULL;

    while (curline != NULL) {
        StripCrLf(curline, &nextline);
        unsigned int nCurLineLen = strlen(curline);
        m_pArray[row]->InsertTextAt(col, curline);
        if (nextline) {
            if (InsertOneLineAfter (row) != 0) {
                return -1;
            }
            m_pArray[row+1]->InsertTextAt(0, m_pArray[row]->GetArrayPtr() + col + nCurLineLen);
            m_pArray[row]->RemoveText(col+nCurLineLen, EOL);
            col = 0;
            row ++;
        } else {
            col += nCurLineLen;
        }
        curline = nextline;
    }
    if (newrow) {
	    *newrow = row;
    }
    if (newcol) {
	    *newcol = col;
    }
    return 0;
}

#define CHECK_POSITIONS(r0,c0,r1,c1) \
    do { \
        r0 = (r0 == LAST_LINE ? m_nValidLines -1 : r0); \
        r1 = (r1 == LAST_LINE ? m_nValidLines -1 : r1);   \
        if (CompareTextPosition (r0, c0, r1, c1) > 0) { \
            /* Swap start and end*/ \
            unsigned int tmp; \
            tmp = r0; \
            r0 = r1; \
            r1 = tmp; \
            tmp = c0; \
            c0 = c1; \
            c1 = tmp; \
        } \
        assert (r1 < m_nValidLines && r0 <= r1); \
    } while (0)


int CTextDoc::RemoveText (unsigned int rowStart, unsigned colStart, unsigned rowEnd, unsigned int colEnd)
{
    CHECK_POSITIONS(rowStart, colStart, rowEnd, colEnd);
    if (m_stack) {
        unsigned int len = GetTextLength(rowStart, colStart, rowEnd, colEnd) + 1;
        char* txt = (char*) malloc (len+1);
        if (!txt) {
            return -1;
        }
        GetText(rowStart, colStart, rowEnd, colEnd, txt, len);
        CEditOperation* op = new CEditOperation;
        op->m_text = txt;
        op->m_startline = rowStart;
        op->m_startcol = colStart;
        op->m_endline = rowEnd;
        op->m_endcol = colEnd;
        op->m_operation = ::Remove;
        m_stack->AddOperation(op);
    }

    RemoveTextHelper(rowStart, colStart, rowEnd, colEnd);
    return 0;
}

void CTextDoc::RemoveTextByStack (unsigned int rowStart, unsigned colStart, 
                         unsigned int rowEnd, unsigned int colEnd)
{
    CHECK_POSITIONS(rowStart, colStart, rowEnd, colEnd);
    RemoveTextHelper(rowStart, colStart, rowEnd, colEnd);
}

void CTextDoc::RemoveTextHelper(unsigned int rowStart, unsigned colStart, unsigned int rowEnd, unsigned int colEnd)
{
    if (rowStart == rowEnd) {
        RemoveTextInLine (rowStart, colStart, colEnd);
        if (m_pBkPtMgr) {
           m_pBkPtMgr->AdjustBreakPoints(false, rowStart+1);
        }
        return;
    }
    
    // first remove all the full line between rowStart and rowEnd.
    unsigned int i;
    assert (rowStart+1 < m_nValidLines);
    for (i = rowStart+1; i <= rowEnd -1 ; i++) {
        delete m_pArray[i];
    }
    if (rowStart + 1 <= rowEnd -1) {
        Remove(rowStart+1, rowEnd-1);
        if (m_pBkPtMgr) {
           m_pBkPtMgr->AdjustBreakPoints(false, rowStart+2);
        }
        m_nValidLines -= (rowEnd - rowStart -1); 
    }
    // Then remove (rowStart, colStart) --> (rowStart+1, colEnd)
    RemoveTextInLine(rowStart+1, 0, colEnd);
    if (m_pBkPtMgr && colEnd == EOL) {
       m_pBkPtMgr->AdjustBreakPoints(false, rowStart+2);
    }
    RemoveTextInLine(rowStart, colStart, EOL);
    if (m_pBkPtMgr) {
       m_pBkPtMgr->AdjustBreakPoints(false, rowStart+1);
    }

    if (m_nCount - m_nValidLines > DOC_GROW_STEP) {
        Resize ((m_nValidLines/DOC_GROW_STEP +1) * DOC_GROW_STEP);
    }
    for (i = m_nValidLines; i < m_nCount; i++) {
        m_pArray[i] = NULL;
    }
}


void CTextDoc::RemoveLeadingSpacesBefore(unsigned int row, unsigned int idx)
{
    assert (row < m_nValidLines);
    unsigned int uToBeRemoved = m_pArray[row]->GetIdxForRemoveLeadingSpacesBefore(idx);
    if (uToBeRemoved == EOL) {
        return;
    }
    char* buf = (char*) malloc(uToBeRemoved + 2);
    buf[uToBeRemoved + 1] = '\0';
    char* linetext = GetLineText(row);
    for (unsigned int i = 0; i <= uToBeRemoved; i++) {
        buf[i] = linetext[i];
    }
    CEditOperation* op = new CEditOperation;
    op->m_operation = ::Remove;
    op->m_startline = row;
    op->m_startcol = 0;
    op->m_endline = row;
    op->m_endcol = uToBeRemoved;
    op->m_text = buf;

    m_stack->AddOperation(op);

    m_pArray[row]->RemoveLeadingSpacesBefore(idx);
}

void CTextDoc::RemoveTextInLine(unsigned int r, unsigned int from, unsigned int to)
{
    assert (r < m_nValidLines);
    assert (from <= to);
    assert (to <= strlen(GetLineText(r)) || to == EOL);
    assert (m_pArray[r]);
    
    m_pArray[r]->RemoveText(from, to);

    if (to == EOL && r+1 < m_nValidLines) {
        /* copy the remaining to the next line and remove current line*/
        assert (r+1 < m_nValidLines && m_pArray[r+1]);
        m_pArray[r+1]->InsertTextAt(0, GetLineText(r));
        delete m_pArray[r];
        Remove(r,r);
        assert (m_nValidLines >= 1);
        m_nValidLines --;
    }

    if (m_nValidLines == 0) {
        /* We need at least one valid line to hold the caret.*/
        assert (r == 0);
        assert (m_pArray[0]);
        m_pArray[0]->RemoveText(0, EOL);
    }
}

void CTextDoc::SetCrLf(const char* crlf)
{
    memset (m_crlf, 0, 3);
    strncpy (m_crlf, crlf, 2);
}
    
void CTextDoc::SetStack(CEditStack* stack)
{
    m_stack = stack;
}

void CTextDoc::SetBkPtMgr(IBkPtMgr* mgr)
{
   m_pBkPtMgr = mgr;
}

char* CTextDoc::GetLineText(unsigned int row)
{
    row = (row == LAST_LINE ? m_nValidLines -1 : row);
    assert (row < m_nValidLines);
    return m_pArray[row]->GetArrayPtr();
}

int CTextDoc::InsertOneLineAfter (unsigned int row)
{
    row = (row == LAST_LINE ? m_nValidLines -1 : row);
    assert (row < m_nValidLines);

    if (m_nValidLines +1 > m_nCount) {
        if ( Resize (m_nCount + DOC_GROW_STEP) != 0) {
            return -1;
        }
        unsigned int i;
        for (i = m_nValidLines; i < m_nCount; i++) {
            m_pArray[i] = NULL;
        }
    }

    CTextLine* newline = new CTextLine;
    InsertAt(row+1, 1, &newline);
    m_nValidLines ++;
    if (m_pBkPtMgr) {
       m_pBkPtMgr->AdjustBreakPoints(true, row+1);
    }
    return 0;
}

bool CTextDoc::FindInSelectionForward (CTextSelection* sel_org, const char* str,  CTextSelection* sel_found)
{
    unsigned int found_idx, l;
    unsigned int sl, sc, bl, bc;

    assert (sel_org);
    assert (sel_found);
    
    sel_found->SetSelection(0,0,0,0);

    if (!str || str[0] == '\0') {
        return false;
    }

    if (!sel_org->GetAscendantSel(&sl, &sc, &bl,&bc)) {
        return false;
    }
    
    bl =  (bl == LAST_LINE ? m_nValidLines -1 : bl); 
    sl =  (sl == LAST_LINE ? m_nValidLines -1 : sl); 

    assert (sl < m_nValidLines && bl < m_nValidLines);
    CTextLine* line = GetLineObject(sl);
    assert (line);
    if (sl == bl) {
        if (line->FindInLineForward(sc, bc, str, &found_idx)) {
            sel_found->SetSelection(sl, found_idx, sl, found_idx+strlen(str));
            return true;
        }
        return false;
    }
    
    if (line->FindInLineForward(sc, EOL, str, &found_idx)) {
        sel_found->SetSelection(sl, found_idx, sl, found_idx+strlen(str));
        return true;
    }
    for (l = sl + 1; l < bl ; l++) {
        line = GetLineObject(l);
        assert (line);
        if (line->FindInLineForward(0, EOL, str, &found_idx)) {
            sel_found->SetSelection(l, found_idx, l, found_idx+strlen(str));
            return true;
        }
    }

    line = GetLineObject(bl);
    assert (line);
    if (line->FindInLineForward(0, bc, str, &found_idx)) {
        sel_found->SetSelection(bl, found_idx, bl, found_idx+strlen(str));
        return true;
    }
    return false;
}

bool CTextDoc::FindInSelectionBackward (CTextSelection* sel_org, const char* str,  CTextSelection* sel_found)
{
    unsigned int found_idx, l;
    unsigned int sl, sc, bl, bc;

    assert (sel_org);
    assert (sel_found);
    
    sel_found->SetSelection(0,0,0,0);

    if (!str || str[0] == '\0') {
        return false;
    }

    if (!sel_org->GetAscendantSel(&sl, &sc, &bl,&bc)) {
        return false;
    }
    
    bl =  (bl == LAST_LINE ? m_nValidLines -1 : bl); 
    sl =  (sl == LAST_LINE ? m_nValidLines -1 : sl); 

    assert (sl < m_nValidLines && bl < m_nValidLines);
    CTextLine* line = GetLineObject(sl);
    assert (line);
    if (sl == bl) {
        if (line->FindInLineBackward(sc, bc, str, &found_idx)) {
            sel_found->SetSelection(sl, found_idx, sl, found_idx+strlen(str));
            return true;
        }
        return false;
    }
    
    line = GetLineObject(bl);
    assert (line);
    if (line->FindInLineBackward(0, bc, str, &found_idx)) {
        sel_found->SetSelection(bl, found_idx, bl, found_idx+strlen(str));
        return true;
    }
    for (l = bl - 1; l > sl ; l--) {
        line = GetLineObject(l);
        assert (line);
        if (line->FindInLineBackward(0, EOL, str, &found_idx)) {
            sel_found->SetSelection(l, found_idx, l, found_idx+strlen(str));
            return true;
        }
    }
    line = GetLineObject(sl);
    if (line->FindInLineBackward(sc, EOL, str, &found_idx)) {
        sel_found->SetSelection(sl, found_idx, sl, found_idx+strlen(str));
        return true;
    }

    return false;
}

void CTextDoc::SetDirty(bool dirty/*=true*/)
{
    m_dirty = dirty;
}

bool CTextDoc::IsDirty()
{
    return m_dirty;
}

void CTextDoc::CloseDoc()
{
    RemoveText(0, 0, LAST_LINE, EOL);
    SetDirty(false);
}

size_t CTextDoc::GetTextLength (unsigned int rowStart, unsigned colStart, 
                         unsigned rowEnd, unsigned int colEnd)
{
    assert (strlen(m_crlf) != 0);
    CHECK_POSITIONS(rowStart, colStart, rowEnd, colEnd);
    assert (colStart <= strlen(GetLineText(rowStart)) || colStart == EOL);
    assert (colEnd <= strlen(GetLineText(rowEnd)) || colEnd == EOL);

    size_t size = 0;
    
    if (rowStart == rowEnd) {
        assert (! (colEnd == EOL && colStart == EOL));
        assert (! (colStart == strlen(GetLineText(rowStart)) && colEnd == colStart));

        size = colEnd - colStart + 1;
        if (colEnd == EOL) {
            size = strlen(GetLineText(rowStart)) - colStart + strlen(m_crlf);
        }
        return size;
    }
    
    // first calculate the chars between rowStart and rowEnd.
    unsigned int i;
    for (i = rowStart+1; i < rowEnd; i++) {
        size += strlen(GetLineText(i)) + strlen(m_crlf);
    }
    // adding chars in rowStart.
    if (colStart != EOL) {
        assert (colStart <= strlen(GetLineText(rowStart)));
        size += strlen(GetLineText(rowStart)) - colStart + strlen(m_crlf);
    }
    if (colEnd != EOL) {
        assert (colEnd <= strlen(GetLineText(rowEnd)));
        size += colEnd +1;
    } else {
        size += strlen(GetLineText(rowEnd));
        if (rowEnd != m_nValidLines -1) {
            size += strlen(m_crlf);
        }
    }
    return size;
}

size_t CTextDoc::GetText (unsigned int rowStart, unsigned colStart, 
                         unsigned rowEnd, unsigned int colEnd, char* buf, size_t size)
{
    unsigned int copiedSize = 0;
    assert (strlen(m_crlf) != 0);
    CHECK_POSITIONS(rowStart, colStart, rowEnd, colEnd);
    assert (size > GetTextLength(rowStart, colStart, rowEnd, colEnd) );
    assert (colStart <= strlen(GetLineText(rowStart)) || colStart == EOL);
    assert (colEnd <= strlen(GetLineText(rowEnd)) || colEnd == EOL);
    
    memset (buf, 0, size);
    if (rowStart == rowEnd) {
        assert (! (colEnd == EOL && colStart == EOL));
        assert (! (colStart == strlen(GetLineText(rowStart)) && colEnd == colStart));
        
        bool addNewLine = (colEnd == EOL);
        colEnd = (addNewLine ? strlen(GetLineText(rowStart)) -1 : colEnd);

        copiedSize = colEnd - colStart + 1;
        strncpy (buf, GetLineText(rowStart)+colStart, copiedSize);

        if (addNewLine) {
            copiedSize += strlen(m_crlf);
            strcat(buf, m_crlf);
        }
        return copiedSize;
    }

    // adding chars in rowStart.
    if (colStart != EOL) {
        assert (colStart <= strlen(GetLineText(rowStart)));
        copiedSize = strlen(GetLineText(rowStart)) - colStart;
        strcat (buf, GetLineText(rowStart)+colStart);
        copiedSize += strlen (m_crlf);
        strcat(buf, m_crlf);
    }
    //  copy the chars between rowStart and rowEnd.
    unsigned int i;
    for (i = rowStart+1; i < rowEnd; i++) {
        copiedSize += strlen(GetLineText(i)) + strlen(m_crlf);
        strcat (buf, GetLineText(i));
        strcat (buf, m_crlf);
    }
    if (colEnd != EOL) {
        assert (colEnd <= strlen(GetLineText(rowEnd)));
        strncpy (buf + copiedSize, GetLineText(rowEnd), colEnd+1);
        copiedSize += colEnd +1;
    } else {
        strcat (buf, GetLineText(rowEnd));
        copiedSize += strlen(GetLineText(rowEnd));
        if (rowEnd != m_nValidLines -1) {
            strcat (buf, m_crlf);
            copiedSize += strlen(m_crlf);
        }
    }    
    return copiedSize;
}

#define READ_BUF_SIZE 1024
void CTextDoc::ReadFile(const char* filename)
{
    char buf[READ_BUF_SIZE];
    assert(filename);
    FILE* f = fopen(filename, "rb");
    if (!f) {
        return;
    }

    RemoveText(0,0, LAST_LINE, EOL);
    memset (buf, 0, READ_BUF_SIZE);
    while (fread(buf, 1, READ_BUF_SIZE-1, f) != 0) {
        InsertTextAt (LAST_LINE, EOL, buf);
        memset (buf, 0, READ_BUF_SIZE);
    }
    return;
}

void CTextDoc::WriteFile(const char* filename)
{
    assert (strlen(m_crlf) != 0);
    assert (filename);
    FILE* f = fopen(filename, "wb");
    if (!f) {
        return;
    }
    
    fwrite(GetLineText(0), 1, strlen(GetLineText(0)), f);
    unsigned int i;
    for (i = 1; i < m_nValidLines; i++) {
        fwrite (m_crlf, 1, strlen(m_crlf), f);
        fwrite(GetLineText(i), 1, strlen(GetLineText(i)), f);
    }
    fclose(f);
    return;
}

unsigned int CTextDoc::GetMaxLineLength()
{
    unsigned int max = 0;
    unsigned int i = 0;

    for (i = 0; i < m_nValidLines; i++) {
        size_t len = strlen(GetLineText(i));
        max = (max > len ? max : len);
    }
    return max;
}

CTextLine* CTextDoc::GetLineObject(unsigned int line)
{
    assert (line < m_nValidLines || line == LAST_LINE);
    line = (line == LAST_LINE ? m_nValidLines-1 : line);
    return m_pArray[line];
}

/* StripCrLf strip 0x0a, 0x0d, 0x0a0d and 0x0d0a from "text" by setting those chars to null.
 *  After calling StripCrLf, "text" can be used directly for the first line of text.
 *  Using *nexttext as input for following call until *nexttext is NULL.
 */

void StripCrLf (char* text, char** nexttext)
{
    unsigned int i = 0;
    unsigned int len = strlen(text);
    char crlf0;

    for (i = 0; i < len; i++) {
        if (text[i] == 0x0a || text[i] == 0x0d) {
            crlf0 = text[i];
            text[i] = '\0';
            *nexttext = text + i +1;
            break;
        }
    }
    if (i == len) {
        *nexttext = NULL;
        return;
    }

    /* still check the 2nd half*/
    char crlf1 = (crlf0 == 0x0a ? 0x0d : 0x0a);
    if ( text[++i] == crlf1) {
        *nexttext = text + i + 1;
    }
    return;
}

int CompareTextPosition (unsigned int r1, unsigned int c1, unsigned int r2, unsigned int c2)
{
    if (r1 > r2) {
        return 1;
    }

    if (r1 < r2) {
        return -1;
    }

    assert (r1 == r2);
    if (c1 > c2) {
        return 1;
    }
    if (c1 < c2) {
        return -1;
    }
    assert (c1 == c2);
    return 0;
}

CTextSelection::CTextSelection ()
{
    m_nStartLine = 0;
    m_nStartCol = 0;
    m_nEndLine = 0;
    m_nEndCol = 0;
}

bool CTextSelection::InSelection(unsigned int line, unsigned int col)
{
    if (!Selected()) {
        return false;
    }
    return (ComparePosWithMe(line, col) == 0);
}

/*
 * Return 0, if in selection; -1, if prior to selection; 1 if after selectrion
 */
int CTextSelection::ComparePosWithMe (unsigned int l, unsigned int c)
{
	unsigned int  small_l, small_c, big_l, big_c;
	if (CompareTextPosition (m_nStartLine, m_nStartCol, m_nEndLine, m_nEndCol) <= 0) {
		small_l = m_nStartLine;
		small_c = m_nStartCol;
		big_l = m_nEndLine;
		big_c = m_nEndCol;
	} else {
		small_l = m_nEndLine;
		small_c = m_nEndCol;
		big_l = m_nStartLine;
		big_c = m_nStartCol;
	}

    assert (CompareTextPosition (small_l, small_c, big_l, big_c) <= 0);

    if (CompareTextPosition (l, c, small_l, small_c) < 0) {
        return -1;
    }
    if (CompareTextPosition (l, c, big_l, big_c) >= 0 ) {
        return 1;
    }
    return 0;
}

bool CTextSelection::Selected ()
{
    return (CompareTextPosition(m_nStartLine, m_nStartCol, m_nEndLine, m_nEndCol) != 0);
}

void CTextSelection::SetStart (unsigned int l, unsigned int c)
{
	m_nStartLine = l;
	m_nStartCol = c;
}

void CTextSelection::GetStart (unsigned int* l, unsigned int* c)
{
	*l = m_nStartLine;
	*c = m_nStartCol;
}

void CTextSelection::SetEnd (unsigned int l, unsigned int c)
{
	m_nEndLine = l;
	m_nEndCol = c;
}

void CTextSelection::GetEnd (unsigned int* l, unsigned int* c)
{
	*l = m_nEndLine;
	*c = m_nEndCol;
}

void CTextSelection::SetSelection (unsigned int l1, unsigned int c1, unsigned int l2, unsigned int c2)
{
    m_nStartLine = l1;
    m_nStartCol = c1;
    m_nEndLine = l2;
    m_nEndCol = c2;
    return;
}

void CTextSelection::GetSelection (unsigned int* l1, unsigned int* c1, unsigned int* l2, unsigned int* c2)
{
    *l1 = m_nStartLine;
    *c1 = m_nStartCol;
    *l2 = m_nEndLine;
    *c2 = m_nEndCol;
}

bool CTextSelection::GetAscendantSel (unsigned int* sl, unsigned int* sc, unsigned int* bl, unsigned int* bc)
{
    if (!Selected()) {
        return false;
    }

	if (CompareTextPosition (m_nStartLine, m_nStartCol, m_nEndLine, m_nEndCol) <= 0) {
		*sl = m_nStartLine;
		*sc = m_nStartCol;
		*bl = m_nEndLine;
		*bc = m_nEndCol;
	} else {
		*sl = m_nEndLine;
		*sc = m_nEndCol;
		*bl = m_nStartLine;
		*bc = m_nStartCol;
	}
    return true;
}

bool CTextSelection::GetAscendantInclusiveSel (unsigned int* sl, unsigned int* sc, unsigned int* bl, unsigned int* bc)
{
    if (!GetAscendantSel(sl,sc,bl,bc)) {
        return false;
    }

    if (*bc > 0 && *bc != EOL) {
        -- (*bc);
    } else if (*bc == 0) {
        assert (*bl);
        (*bl)--;
        assert (*bl >= *sl);
        *bc = EOL;
    }
    return true;
}

//---------------------------------------------
CSelections::CSelections() : CVarSel(SEL_INIT)
{
    unsigned int i;
    for (i = 0; i < m_nCount; i++) {
        m_pArray[i] = NULL;
    }
    m_uSelCount = 0;
}

CSelections::~CSelections()
{
    RemoveAll();
}

int CSelections::AddSel(CTextSelection* sel)
{
    assert (m_uSelCount <= m_nCount);
    if (m_uSelCount == m_nCount) {
        int ret = Resize(m_nCount + SEL_GROW);
        if (ret < 0) {
            return ret;
        }
    }

    assert (m_uSelCount < m_nCount);
    m_pArray[m_uSelCount] = sel;
    return ((int) m_uSelCount++);
}

void CSelections::RemoveAll()
{
    unsigned int i;
    for (i = 0; i < m_uSelCount; i++) {
        if (m_pArray[i]) {
            delete (m_pArray[i]);
            m_pArray[i] = NULL;
        }
    }
    Resize(SEL_INIT);
    m_uSelCount = 0;
}
